package com.yingxue.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yingxue.demo.constant.Constant;
import com.yingxue.demo.entity.User;
import com.yingxue.demo.exception.BizException;
import com.yingxue.demo.exception.enums.BadRequestExceptionEnum;
import com.yingxue.demo.exception.enums.UnauthorizedExceptionEnum;
import com.yingxue.demo.mapper.UserMapper;
import com.yingxue.demo.service.UserService;
import com.yingxue.demo.util.JWTUtil;
import com.yingxue.demo.util.PasswordUtil;
import com.yingxue.demo.vo.req.LoginReqVO;
import com.yingxue.demo.vo.resp.LoginRespVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Package: com.company.scaffold.service.impl
 * @Author: Mr.Waves
 * @Date: 2020-03-17 23:49
 * @Description:
 **/
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public LoginRespVO login(LoginReqVO loginReqVO) {
        // 声明放最前
        LoginRespVO loginRespVO = new LoginRespVO();
        Map<String, Object> claims = new HashMap<>();
        // 第一步：使用用户名查询账号信息
        User user = getUserByUsername(loginReqVO.getUsername());
        if (null == user) {
            throw new BizException(BadRequestExceptionEnum.ACCOUNT_NOT_EXIST);
        }
        if (user.getStatus() == 2) {
            throw new BizException(UnauthorizedExceptionEnum.ACCOUNT_LOCKED);
        }
        if (!PasswordUtil.match(user.getSalt(), loginReqVO.getPassword(), user.getPasswordCipher(), 5)) {
            throw new BizException(BadRequestExceptionEnum.ACCOUNT_PASSWORD_NOT_MATCH);
        }
        // 第二步：生成accessToken和refreshToken
        claims.put(Constant.JWT_ROLES_KEY, getRolesByUserId(user.getId()));
        claims.put(Constant.JWT_PERMISSIONS_KEY, getPermissionsByUserId(user.getId()));
        claims.put(Constant.JWT_USER_NAME_KEY, user.getUsername());
        String accessToken = JWTUtil.getAccessToken(String.valueOf(user.getId()), claims);
        String refreshToken = null;
        if (loginReqVO.getType().equals("pc")) {
            refreshToken = JWTUtil.getPCRefreshToken(String.valueOf(user.getId()), claims);
        }
        if (loginReqVO.getType().equals("app")) {
            refreshToken = JWTUtil.getAPPRefreshToken(String.valueOf(user.getId()), claims);
        }
        // 第三步：封装数据并返回
        loginRespVO.setId(String.valueOf(user.getId()));
        loginRespVO.setPhone(user.getPhone());
        loginRespVO.setUsername(user.getUsername());
        loginRespVO.setAccessToken(accessToken);
        loginRespVO.setRefreshToken(refreshToken);
        return loginRespVO;
    }

    @Override
    public User getUserByUsername(String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username).eq("deleted", 1);
        User user = userMapper.selectOne(queryWrapper);
        return user;
    }

    @Override
    public void logout(String accessToken, String refreshToken) {
        if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) {
            throw new BizException(UnauthorizedExceptionEnum.TOKEN_MISSING);
        }
        Subject subject = SecurityUtils.getSubject();
        log.info("subject.getPrincipals()={}",subject.getPrincipals());
        if (subject.isAuthenticated()) {
            subject.logout();
        }
        String userId = JWTUtil.getUserId(accessToken);
        /*
         * 把accessToken加入黑名单 禁止再登录
         */
        redisTemplate.opsForValue().set(Constant.JWT_ACCESS_TOKEN_BLACKLIST + accessToken, userId, JWTUtil.getRemainingTime(accessToken), TimeUnit.MILLISECONDS);

        /*
         * 把refreshToken加入黑名单 禁止再拿来刷新token
         */
        redisTemplate.opsForValue().set(Constant.JWT_REFRESH_TOKEN_BLACKLIST + refreshToken, userId, JWTUtil.getRemainingTime(refreshToken), TimeUnit.MILLISECONDS);

    }

    @Override
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }

    /*
     * mock角色数据
     */
    private Set<String> getRolesByUserId(Long userId) {
        HashSet<String> roles = new HashSet<>();
        if (userId.equals(1245184032447148034L)) {
            roles.add("admin");
        }
        if (userId.equals(1245184501852712962L)) {
            roles.add("test");
        }
        return roles;
    }

    /*
     * mock权限数据
     */
    private Set<String> getPermissionsByUserId(Long userId) {
        HashSet<String> permissions = new HashSet<>();
        if (userId.equals(1239937702799908866L)) {
            permissions.add("sys:user:get");
            permissions.add("sys:user:list");
            permissions.add("sys:user:save");
            permissions.add("sys:user:update");
            permissions.add("sys:user:delete");
        }
        if (userId.equals(1239937997877592065L)) {
            permissions.add("sys:user:list");
        }
        return permissions;
    }
}
